import WebSocket, { WebSocketServer } from 'ws';
import { getLocalIP } from './localip.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { getRandomColor, getUUID } from './common.js';
import { log } from 'console';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

let portConfig = {
    webPort: null,
    wsPort: null
};
let randomColorMap = new Map();
let fileChunks = new Map(); // 存储文件切片
let fileIndex = new Map(); // 已完成文件索引: fileHash -> { fileLink, fileName, fileSize }
let fileIndexPath = path.resolve(__dirname, '..', 'public', 'fileIndex.json');
let server;

const initWebSocket = (webPort, wsPort) => {
    portConfig.webPort = webPort;
    server = new WebSocketServer({
        port: wsPort, 
        host: '0.0.0.0',
        maxPayload: 50 * 1024 * 1024 // 50MB
    });
    
    server.on('close', handleClose);
    server.on('error', handleError);
    server.on('connection', handleConnection);
    console.log('WebSocket server start');
    console.log(`WebSocket server listening on ws://127.0.0.1:${wsPort}`);
    console.log(`WebSocket server listening on ws://${getLocalIP()}:${wsPort}`);

    // 尝试加载已存在的文件索引
    loadFileIndex();
};

const handleClose = () => {
  console.log('WebSocket server closed');
};

const handleError = () => {
  console.log('WebSocket server error');
};

const handleConnection = (socket) => {
  socket.on('message', (data, isBinary) => handleMessage(socket, data, isBinary));
  console.log('New WebSocket connection established');
  // 发送服务器信息（提供HTTP端口给前端）
  try {
    sendToClient(socket, JSON.stringify({ type: 'server-info', webPort: portConfig.webPort }));
  } catch (e) {}
};

const handleMessage = (socket, data, isBinary) => {
  try {
    const jsonData = JSON.parse(data);
    // 记录该连接的用户ID，便于定向回复
    if (jsonData && jsonData.userId) {
      socket.userId = jsonData.userId;
    }
    
    processMessage(jsonData, socket);
  } catch (e) {
    console.error('Error parsing JSON message:', e);
  }
};

const processMessage = (jsonData, socket) => {
    // 根据消息类型处理
    switch(jsonData.type) {
        case 'file-check': {
            const hashString = String(jsonData.fileHash);
            if (fileIndex.has(hashString)) {
                // 构建完整的文件URL
                const fullFilePath = `http://127.0.0.1:${portConfig.webPort}${fileIndex.get(hashString).fileLink}`;
                // 仅回复请求方，避免广播噪声
                sendToClient(socket, JSON.stringify({
                    type: 'file-exists',
                    fileHash: hashString,
                    filePath: fullFilePath,
                    fileName: fileIndex.get(hashString).fileName,
                    fileSize: fileIndex.get(hashString).fileSize
                }));
            } else {
                sendToClient(socket, JSON.stringify({
                    type: 'file-not-exists',
                    fileHash: hashString
                }));
            }
            break;
        }
        case 'file-start':
            // 广播文件开始上传的消息给所有用户（包括发送者）
            console.log(`📢 广播file-start给所有用户: ${jsonData.fileName}`);
            broadcastMessage(JSON.stringify({
                ...jsonData,
                type: 'file-start'
            }));
            // 如果文件已存在，直接广播完成，触发"秒传"
            if (fileIndex.has(String(jsonData.fileHash))) {
                console.log(`⚡ 检测到秒传文件: ${jsonData.fileName}`);
                const exist = fileIndex.get(String(jsonData.fileHash));
                // 延迟一点时间再发送file-complete，让其他用户先看到file-start
                setTimeout(() => {
                    broadcastFileLink(jsonData, exist.fileLink, exist.fileSize);
                }, 100);
            }
            break;
            
        case 'file-chunk':
            console.log(`📦 接收文件分片: ${jsonData.fileName} - 分片 ${jsonData.chunkIndex + 1}/${jsonData.totalChunks}`);
            handleFileChunk(jsonData);
            // 广播上传进度给所有用户
            broadcastProgress(jsonData);
            break;
            
        default:
            // 普通消息
            broadcastMessage(JSON.stringify({
                ...jsonData
            }));
    }
};

// 广播上传进度
const broadcastProgress = (jsonData) => {
    const progress = Math.round((jsonData.chunkIndex + 1) / jsonData.totalChunks * 100);
    broadcastMessage(JSON.stringify({
        type: 'file-progress',
        fileHash: jsonData.fileHash,
        progress: progress
    }));
};

// 修改广播文件链接函数
const broadcastFileLink = (jsonData, fileLink, actualFileSize = null) => {
    const userColor = randomColorMap.get(jsonData.userId);
    
    // 构建完整的文件URL
    const fullFilePath = `http://127.0.0.1:${portConfig.webPort}${fileLink}`;
    
    const response = JSON.stringify({
        type: 'file-complete',
        filePath: fullFilePath,
        fileName: jsonData.fileName,
        dateTime: jsonData.dateTime,
        username: jsonData.username,
        fileSize: actualFileSize || jsonData.fileSize, // 使用实际文件大小
        randomColor: userColor,
        fileHash: jsonData.fileHash,
        progress: 100 // 确保进度设置为100%
    });
    console.log(`📤 广播文件完成消息: ${jsonData.fileName} -> ${fullFilePath}`);
    broadcastMessage(response);
};

// 修改处理文件切片的函数
const handleFileChunk = async (jsonData) => {
    try {
        const { fileHash, chunkIndex, totalChunks, fileData, fileName } = jsonData;
        const hashString = String(fileHash);
        
        console.log(`📦 接收文件分片: ${fileName} - 分片 ${chunkIndex + 1}/${totalChunks}`);
        
        // 初始化文件切片存储
        if (!fileChunks.has(hashString)) {
            fileChunks.set(hashString, {
                chunks: new Array(totalChunks),
                receivedChunks: 0,
                fileName,
                metadata: jsonData,
                tempDir: path.join(__dirname, '..', 'temp', hashString)
            });

            // 创建临时目录
            if (!fs.existsSync(fileChunks.get(hashString).tempDir)) {
                fs.mkdirSync(fileChunks.get(hashString).tempDir, { recursive: true });
            }
        }

        const fileInfo = fileChunks.get(hashString);
        const chunkPath = path.join(fileInfo.tempDir, `chunk_${chunkIndex}`);

        // 保存切片
        const chunkBuffer = Buffer.from(fileData, 'base64');
        fs.writeFileSync(chunkPath, chunkBuffer);
        fileInfo.chunks[chunkIndex] = chunkPath;
        fileInfo.receivedChunks++;

        console.log(`✅ 分片 ${chunkIndex} 保存成功，大小: ${chunkBuffer.length} 字节`);

        // 发送切片接收确认
        sendChunkResponse(jsonData.userId, {
            type: 'chunk-received',
            fileHash: hashString,
            chunkIndex
        });

        // 检查是否所有切片都已接收
        if (fileInfo.receivedChunks === totalChunks) {
            console.log(`🎉 所有分片接收完成，开始合并文件: ${fileName}`);
            await mergeFileChunks(hashString);
        }
    } catch (error) {
        console.error('处理文件切片错误:', error);
        // 发送错误确认
        sendChunkResponse(jsonData.userId, {
            type: 'chunk-error',
            fileHash: String(jsonData.fileHash),
            chunkIndex: jsonData.chunkIndex,
            error: error.message
        });
    }
};

// 发送切片响应
const sendChunkResponse = (userId, response) => {
  server.clients.forEach(client => {
    try {
      if (client.userId === userId) {
        client.send(JSON.stringify(response));
      }
    } catch (e) {
      // ignore send errors
    }
  });
};

// 合并文件切片
const mergeFileChunks = async (fileHash) => {
    try {
        const fileInfo = fileChunks.get(fileHash);
        const publicDir = path.resolve(__dirname, '..', 'public');
        
        if (!fs.existsSync(publicDir)) {
            fs.mkdirSync(publicDir, { recursive: true });
        }

        // 改进文件扩展名处理
        const originalFileName = fileInfo.fileName;
        const lastDotIndex = originalFileName.lastIndexOf('.');
        const extension = lastDotIndex > 0 ? originalFileName.substring(lastDotIndex) : '';
        const fileName = `${getUUID()}${extension}`;
        const filePath = path.resolve(publicDir, fileName);
        const fileLink = `/public/${fileName}`;

        console.log(`开始合并文件: ${originalFileName} -> ${fileName}`);

        // 使用流来合并文件
        const writeStream = fs.createWriteStream(filePath);
        let totalWritten = 0;
        let missingChunks = [];
        
        // 按顺序合并分片
        for (let i = 0; i < fileInfo.chunks.length; i++) {
            const chunkPath = fileInfo.chunks[i];
            if (fs.existsSync(chunkPath)) {
                const chunkData = fs.readFileSync(chunkPath);
                console.log(`合并切片 ${i}, 大小: ${chunkData.length} 字节`);
                writeStream.write(chunkData);
                totalWritten += chunkData.length;
                fs.unlinkSync(chunkPath); // 删除临时切片文件
            } else {
                console.error(`切片文件不存在: ${chunkPath}`);
                missingChunks.push(i);
            }
        }

        writeStream.end();

        // 等待文件写入完成
        await new Promise((resolve, reject) => {
            writeStream.on('finish', () => {
                console.log(`文件合并完成: ${fileName}, 总大小: ${totalWritten} 字节`);
                resolve();
            });
            writeStream.on('error', (err) => {
                console.error('文件写入错误:', err);
                reject(err);
            });
        });

        // 检查是否有缺失的分片
        if (missingChunks.length > 0) {
            throw new Error(`缺失分片: ${missingChunks.join(', ')}`);
        }

        // 检查文件大小是否合理
        const stats = fs.statSync(filePath);
        const originalSize = fileInfo.metadata.fileSize || 'unknown';
        console.log(`最终文件大小: ${stats.size} 字节, 原始大小: ${originalSize} 字节`);
        
        if (stats.size === 0) {
            throw new Error('合并后的文件大小为0');
        }

        // 验证文件大小（如果原始大小可用）
        if (originalSize !== 'unknown' && Math.abs(stats.size - originalSize) > 1024) {
            console.warn(`文件大小不匹配: 原始=${originalSize}, 实际=${stats.size}`);
        }

        // 删除临时目录
        if (fs.existsSync(fileInfo.tempDir)) {
            fs.rmdirSync(fileInfo.tempDir, { recursive: true });
        }

        // 广播文件链接
        broadcastFileLink(fileInfo.metadata, fileLink, stats.size);

        // 清理内存
        fileChunks.delete(fileHash);

        // 记录文件索引并持久化
        try {
            fileIndex.set(String(fileHash), {
                fileLink,
                fileName: fileInfo.fileName,
                fileSize: stats.size // 使用实际文件大小
            });
            persistFileIndex();
            console.log(`文件索引已更新: ${fileInfo.fileName}`);
        } catch (idxErr) {
            console.error('写入文件索引失败:', idxErr);
        }

    } catch (error) {
        console.error('合并文件切片错误:', error);
        
        // 发送错误通知给客户端
        const fileInfo = fileChunks.get(fileHash);
        if (fileInfo) {
            broadcastMessage(JSON.stringify({
                type: 'file-error',
                fileHash: fileHash,
                fileName: fileInfo.fileName,
                error: error.message
            }));
        }
        
        // 清理临时文件和目录
        try {
            if (fileInfo && fs.existsSync(fileInfo.tempDir)) {
                fs.rmdirSync(fileInfo.tempDir, { recursive: true });
            }
        } catch (cleanupError) {
            console.error('清理临时文件错误:', cleanupError);
        }
        fileChunks.delete(fileHash);
    }
};

const broadcastMessage = (message) => {
  server.clients.forEach(client => {
    client.send(message);
  });
};

// 广播给除了发送者之外的所有客户端
const broadcastToOthers = (senderSocket, message) => {
  let sentCount = 0;
  server.clients.forEach(client => {
    // 跳过发送者本身
    if (client !== senderSocket) {
      try {
        client.send(message);
        sentCount++;
      } catch (e) {
        // ignore send errors
      }
    }
  });
  console.log(`📤 已发送给 ${sentCount} 个其他客户端`);
};

const sendToClient = (socket, message) => {
  try {
    socket && socket.send && socket.send(message);
  } catch (e) {
    // ignore
  }
};

// 从磁盘加载文件索引
function loadFileIndex() {
  try {
    if (fs.existsSync(fileIndexPath)) {
      const json = JSON.parse(fs.readFileSync(fileIndexPath, 'utf-8'));
      fileIndex = new Map(Object.entries(json));
      console.log(`已加载文件索引，数量: ${fileIndex.size}`);
    }
  } catch (e) {
    console.error('加载文件索引失败:', e);
  }
}

// 持久化文件索引到磁盘
function persistFileIndex() {
  try {
    const obj = Object.fromEntries(fileIndex);
    const dir = path.dirname(fileIndexPath);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }
    fs.writeFileSync(fileIndexPath, JSON.stringify(obj, null, 2));
  } catch (e) {
    console.error('持久化文件索引失败:', e);
  }
}

export { initWebSocket };